Let us set some global options for all code chunks in this
document.
# Set seed for reproducibility
set.seed(1982)
# Set global options for all code chunks
knitr::opts_chunk$set(
# Disable messages printed by R code chunks
message = TRUE,
# Disable warnings printed by R code chunks
warning = TRUE,
# Show R code within code chunks in output
echo = TRUE,
# Include both R code and its results in output
include = TRUE,
# Evaluate R code chunks
eval = TRUE,
# Enable caching of R code chunks for faster rendering
cache = FALSE,
# Align figures in the center of the output
fig.align = "center",
# Enable retina display for high-resolution figures
retina = 2,
# Show errors in the output instead of stopping rendering
error = TRUE,
# Do not collapse code and output into a single block
collapse = FALSE
)
# Start the figure counter
fig_count <- 0
# Define the captioner function
captioner <- function(caption) {
fig_count <<- fig_count + 1
paste0("Figure ", fig_count, ": ", caption)
}
# Define the function to truncate a number to two decimal places
truncate_to_two <- function(x) {
floor(x * 100) / 100
}
my.get.roots <- function(order, beta) {
mt <- get(paste0("m", order, "table"))
rb <- rep(0, order + 1)
rc <- rep(0, order)
if(order == 1) {
rc = approx(mt$beta, mt[[paste0("rc")]], beta)$y
} else {
rc = sapply(1:order, function(i) {
approx(mt$beta, mt[[paste0("rc.", i)]], beta)$y
})
}
rb = sapply(1:(order+1), function(i) {
approx(mt$beta, mt[[paste0("rb.", i)]], xout = beta)$y
})
factor = approx(mt$beta, mt$factor, xout = beta)$y
return(list(rb = rb, rc = rc, factor = factor))
}
m1table <- rSPDE:::m1table
m2table <- rSPDE:::m2table
m3table <- rSPDE:::m3table
m4table <- rSPDE:::m4table
poly_from_roots <- function(roots) {
coef <- 1
for (r in roots) {
coef <- convolve(coef, c(1, -r), type = "open")
}
return(rev(coef))
}
compute_sum_poly <- function(factor, pr_roots, pl_roots, cte) {
# Coefficients of p(x) and q(x)
pr_coef <- poly_from_roots(pr_roots)
pl_coef <- poly_from_roots(pl_roots)
# Pad to equal length
max_len <- max(length(pr_coef), length(pl_coef))
pr_coef <- c(rep(0, max_len - length(pr_coef)), pr_coef)
pl_coef <- c(rep(0, max_len - length(pl_coef)), pl_coef)
# Compute r(x)
pr_plus_pl_coef <- factor * pr_coef + cte * pl_coef
# Roots of r(x)
pr_plus_pl_roots <- polyroot(rev(pr_plus_pl_coef))
return(list(new_factor = pr_plus_pl_coef[1], pr_plus_pl_roots = Re(pr_plus_pl_roots)))
}
my.fractional.operators <- function(L, # kappa^2C + G
beta,
C,
scale.factor, # kappa^2
m = 1,
time_step) {
C <- Matrix::Diagonal(dim(C)[1], rowSums(C)) # lumped
Ci <- Matrix::Diagonal(dim(C)[1], 1 / rowSums(C)) # lumped
I <- Matrix::Diagonal(dim(C)[1])
L <- L / scale.factor # C + G/kappa^2
LCi <- L %*% Ci
roots <- my.get.roots(m, beta)
Pl.roots <- roots$rb
Pr.roots <- roots$rc
factor <- roots$factor
new_factor_and_roots <- compute_sum_poly(factor, Pr.roots, Pl.roots, time_step)
new_roots <- new_factor_and_roots$pr_plus_pl_roots
new_factor <- new_factor_and_roots$new_factor
Pr_plus_Pl.factors <- list()
Pr_plus_Pl.factors[[1]] <- I - LCi * new_roots[1]
if (length(new_roots) > 1) {
for (i in 2:length(new_roots)) {
Pr_plus_Pl.factors[[i]] <- I - LCi * new_roots[i]
}
}
Pr.factors <- list()
Pr.factors[[1]] <- I - LCi * roots$rc[1]
if (length(roots$rc) > 1) {
for (i in 2:length(roots$rc)) {
Pr.factors[[i]] <- I - LCi * roots$rc[i]
}
}
#Pr.factors[[length(Pr.factors) + 1]] <- I
output <- list(
Ci = Ci,
C = C,
LCi = LCi,
L = L,
m = m,
beta = beta,
factor = factor,
new_factor = new_factor,
Pr.factors = Pr.factors,
Pr_plus_Pl.factors = Pr_plus_Pl.factors
)
return(output)
}
# install.packages("INLA",repos=c(getOption("repos"),INLA="https://inla.r-inla-download.org/R/testing"), dep=TRUE)
# inla.upgrade(testing = TRUE)
# remotes::install_github("inlabru-org/inlabru", ref = "devel")
# remotes::install_github("davidbolin/rspde", ref = "devel")
# remotes::install_github("davidbolin/metricgraph", ref = "devel")
library(INLA)
## Loading required package: Matrix
## This is INLA_25.05.01 built 2025-05-01 18:43:33 UTC.
## - See www.r-inla.org/contact-us for how to get help.
## - List available models/likelihoods/etc with inla.list.models()
## - Use inla.doc(<NAME>) to access documentation
## Loading required package: fmesher
## This is rSPDE 2.5.1
## - See https://davidbolin.github.io/rSPDE for vignettes and manuals.
## This is MetricGraph 1.4.1
## - See https://davidbolin.github.io/MetricGraph for vignettes and manuals.
##
## Attaching package: 'MetricGraph'
## The following object is masked from 'package:stats':
##
## filter
library(grateful)
library(plotly)
## Loading required package: ggplot2
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
We want to solve the fractional diffusion equation \[\begin{equation}
\label{eq:maineq}
\partial_t u+(\kappa^2-\Delta_\Gamma)^{\frac{\alpha}{2}} u=f \text {
on } \Gamma \times(0, T), \quad u(0)=u_0 \text { on } \Gamma,
\end{equation}\] where \(u\)
satisfies the Kirchhoff vertex conditions \[\begin{equation}
\label{eq:Kcond}
\left\{\phi\in C(\Gamma)\;\Big|\; \forall v\in V:
\sum_{e\in\mathcal{E}_v}\partial_e \phi(v)=0 \right\}
\end{equation}\]
If \(f=0\), then the solution is
given by \[\begin{equation}
\label{eq:sol_reprentation}
u(s,t) =
\displaystyle\sum_{j\in\mathbb{N}}e^{-\lambda^{\frac{\alpha}{2}}_jt}\left(u_0,
e_j\right)_{L_2(\Gamma)}e_j(s).
\end{equation}\]
# Function to build a tadpole graph and create a mesh
gets_graph_tadpole <- function(h){
edge1 <- rbind(c(0,0),c(1,0))
theta <- seq(from=-pi,to=pi,length.out = 100)
edge2 <- cbind(1+1/pi+cos(theta)/pi,sin(theta)/pi)
edges = list(edge1, edge2)
graph <- metric_graph$new(edges = edges)
graph$build_mesh(h = h)
return(graph)
}
Let \(\Gamma_T =
(\mathcal{V},\mathcal{E})\) characterize the tadpole graph with
\(\mathcal{V}= \{v_1,v_2\}\) and \(\mathcal{E}= \{e_1,e_2\}\). The left edge
\(e_1\) has length 1 and the circular
edge \(e_2\) has length 2. As discussed
before, a point on \(e_1\) is
parameterized via \(s=\left(e_1,
t\right)\) for \(t \in[0,1]\)
and a point on \(e_2\) via \(s=\left(e_2, t\right)\) for \(t\in[0,2]\). One can verify that \(-\Delta_\Gamma\) has eigenvalues \(0,\left\{(i \pi / 2)^2\right\}_{i \in
\mathbb{N}}\) and \(\left\{(i \pi /
2)^2\right\}_{2 i \in \mathbb{N}}\) with corresponding
eigenfunctions \(\phi_0\), \(\left\{\phi_i\right\}_{i \in \mathbb{N}}\),
and \(\left\{\psi_i\right\}_{2 i \in
\mathbb{N}}\) given by \(\phi_0(s)=1 /
\sqrt{3}\) and \[\begin{equation*}
\phi_i(s)=C_{\phi, i}\begin{cases}
-2 \sin (\frac{i\pi}{2}) \cos (\frac{i \pi t}{2}), & s \in
e_1, \\
\sin (i \pi t / 2), & s \in e_2,
\end{cases},
\quad
\psi_i(s)=\frac{\sqrt{3}}{\sqrt{2}} \begin{cases}
(-1)^{i / 2} \cos (\frac{i \pi t}{2}), & s \in e_1, \\
\cos (\frac{i \pi t}{2}), & s \in e_2,
\end{cases},
\end{equation*}\] where \(C_{\phi,
i}=1\) if \(i\) is even and
\(C_{\phi, i}=1 / \sqrt{3}\) otherwise.
Moreover, these functions form an orthonormal basis for \(L_2(\Gamma_T)\).
# Function to compute the eigenfunctions
tadpole.eig <- function(k,graph){
x1 <- c(0,graph$get_edge_lengths()[1]*graph$mesh$PtE[graph$mesh$PtE[,1]==1,2])
x2 <- c(0,graph$get_edge_lengths()[2]*graph$mesh$PtE[graph$mesh$PtE[,1]==2,2])
if(k==0){
f.e1 <- rep(1,length(x1))
f.e2 <- rep(1,length(x2))
f1 = c(f.e1[1],f.e2[1],f.e1[-1], f.e2[-1])
f = list(phi=f1/sqrt(3))
} else {
f.e1 <- -2*sin(pi*k*1/2)*cos(pi*k*x1/2)
f.e2 <- sin(pi*k*x2/2)
f1 = c(f.e1[1],f.e2[1],f.e1[-1], f.e2[-1])
if((k %% 2)==1){
f = list(phi=f1/sqrt(3))
} else {
f.e1 <- (-1)^{k/2}*cos(pi*k*x1/2)
f.e2 <- cos(pi*k*x2/2)
f2 = c(f.e1[1],f.e2[1],f.e1[-1],f.e2[-1])
f <- list(phi=f1,psi=f2/sqrt(3/2))
}
}
return(f)
}
Implementation of \(u\)
h <- 0.01
graph <- gets_graph_tadpole(h = h)
## Starting graph creation...
## LongLat is set to FALSE
## Creating edges...
## Setting edge weights...
## Computing bounding box...
## Setting up edges
## Merging close vertices
## Total construction time: 0.16 secs
## Creating and updating vertices...
## Storing the initial graph...
## Computing the relative positions of the edges...
T_final <- 2
time_step <- 0.01
time_seq <- seq(0, T_final, by = time_step)
# Compute the FEM matrices
graph$compute_fem()
G <- graph$mesh$G
C <- graph$mesh$C
I <- Matrix::Diagonal(nrow(C))
x <- graph$mesh$V[, 1]
y <- graph$mesh$V[, 2]
edge_number <- graph$mesh$VtE[, 1]
pos <- sum(edge_number == 1)+1
order_to_plot <- function(v)return(c(v[1], v[3:pos], v[2], v[(pos+1):length(v)], v[2]))
weights <- graph$mesh$weights
# Initial condition
# U_0 <- 10*exp(-((x-1)^2 + (y)^2))
kappa <- 1
alpha <- 0.5 # from 0.5 to 2
m = 2
beta <- alpha/2
L <- kappa^2*C + G
my_op <- my.fractional.operators(L, beta, C, scale.factor = kappa^2, m = m, time_step)
my.solver <- function(obj, v){
Pr.factors <- obj$Pr.factors
Pr_plus_Pl.factors <- obj$Pr_plus_Pl.factors
m <- obj$m
C <- obj$C
Ci <- obj$Ci
factor <- obj$factor
new_factor <- obj$new_factor
if (m==1){
temp <- solve(Pr_plus_Pl.factors[[2]],
Pr.factors[[1]] %*% solve(
Pr_plus_Pl.factors[[1]],
C%*%v))
} else if (m==2){
temp <- solve(Pr_plus_Pl.factors[[3]],
Pr.factors[[2]] %*% solve(
Pr_plus_Pl.factors[[2]],
Pr.factors[[1]] %*% solve(
Pr_plus_Pl.factors[[1]],
C%*%v)))
} else if (m==3){
temp <- solve(Pr_plus_Pl.factors[[4]],
Pr.factors[[3]] %*% solve(
Pr_plus_Pl.factors[[3]],
Pr.factors[[2]] %*% solve(
Pr_plus_Pl.factors[[2]],
Pr.factors[[1]] %*% solve(
Pr_plus_Pl.factors[[1]],
C%*%v))))
} else if (m==4){
temp <- solve(Pr_plus_Pl.factors[[5]],
Pr.factors[[4]] %*% solve(
Pr_plus_Pl.factors[[4]],
Pr.factors[[3]] %*% solve(
Pr_plus_Pl.factors[[3]],
Pr.factors[[2]] %*% solve(
Pr_plus_Pl.factors[[2]],
Pr.factors[[1]] %*% solve(
Pr_plus_Pl.factors[[1]],
C%*%v)))))
}
return((1/factor/new_factor) * Ci %*% temp)
}
op <- fractional.operators(L, beta, C, scale.factor = kappa^2, m = m)
Pl <- op$Pl
Pr <- op$Pr
Ci <- op$Ci
C <- op$C
# Parameters to construct U_0
N_finite <- 4 # choose an even number
adjusted_N_finite <- N_finite + N_finite/2 + 1
EIGENVAL_ALPHA <- NULL
EIGENFUN <- NULL
for (j in 0:N_finite) {
lambda_j_alpha <- (kappa^2 + (j*pi/2)^2)^(alpha/2)
e_j <- tadpole.eig(j,graph)$phi
EIGENVAL_ALPHA <- c(EIGENVAL_ALPHA, lambda_j_alpha)
EIGENFUN <- cbind(EIGENFUN, e_j)
if (j>0 && (j %% 2 == 0)) {
lambda_j_alpha <- (kappa^2 + (j*pi/2)^2)^(alpha/2)
e_j <- tadpole.eig(j,graph)$psi
EIGENVAL_ALPHA <- c(EIGENVAL_ALPHA, lambda_j_alpha)
EIGENFUN <- cbind(EIGENFUN, e_j)
}
}
# Building the initial condition as \sum coeff_j EIGENFUN_j
coeff <- 1*(1:adjusted_N_finite)^-1
coeff[-5] <- 0
# lower_zeroer <- 10 # choose an even number
# adjusted_lower_zeroer <- lower_zeroer + lower_zeroer/2 + 1
# coeff[adjusted_lower_zeroer:adjusted_N_finite] <- 0
U_0 <- EIGENFUN %*% coeff
# Building the true solution as \sum coeff_j EIGENFUN_j e^{-\lambda_j^{\frac{\alpha}{2}}t}
U_true <- matrix(NA, nrow = length(x), ncol = length(time_seq))
for (k in 1:length(time_seq)) {
aux_k <- rep(0, length(x))
for (j in 1:adjusted_N_finite) {
aux_k <- aux_k + exp(-time_seq[k]*EIGENVAL_ALPHA[j])*coeff[j]*EIGENFUN[, j]
}
U_true[, k] <- aux_k
}
U_approx <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx[, 1] <- U_0
# Time-stepping loop
for (k in 1:(length(time_seq) - 1)) {
U_approx[, k + 1] <- as.matrix(my.solver(my_op, U_approx[, k]))
}
{r}
Pr.apply.mult <- function(v){return(Pr.mult(op, v))}
Pl.apply.solve <- function(v){return(Pl.solve(op, v))}
PliC <- apply(C, 2, Pl.apply.solve) # PlC^-1
# Precompute the LHS1 matrix
aux <- apply(PliC, 2, Pr.apply.mult) #PrPl^-1C
LHS <- aux + time_step * Matrix::Diagonal(nrow(C))
# Initialize U matrix to store solution at each time step
U_approx <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx[, 1] <- U_0
# Time-stepping loop
for (k in 1:(length(time_seq) - 1)) {
# Compute the right-hand side for the second equation
RHS <- aux %*% U_approx[, k]
U_approx[, k + 1] <- as.matrix(solve(LHS, RHS))
}
We arrive at the scheme
\[\begin{equation}
(P_r^TC+\tau P_\ell^T)U^{k+1} = P_r^TCU^k
\label{eq:scheme}
\end{equation}\] where \[\begin{equation}
P_r = c_m\prod_{i=1}^m (I-r_{1i}C^{-1}L)\quad\text{and}\quad P_\ell =
b_{m+1}C\prod_{j=1}^{m+1} (I-r_{2j}C^{-1}L)
\end{equation}\] Observe that \[\begin{equation}
P_r^T = c_m\prod_{i=m}^1 (I-r_{1i}LC^{-1})\quad\text{and}\quad P_\ell^T
= b_{m+1}\prod_{j=m+1}^{1} (I-r_{2j}LC^{-1})\cdot C
\end{equation}\] Replacing these two in our scheme we get \[\begin{equation}
\left(c_m\prod_{i=m}^1 (I-r_{1i}LC^{-1})+\tau b_{m+1}\prod_{j=m+1}^{1}
(I-r_{2j}LC^{-1})\right)CU^{k+1} = c_m\prod_{i=m}^1
(I-r_{1i}LC^{-1})\cdot CU^k
\end{equation}\] We can equivalently write this as \[\begin{equation}
\left(\dfrac{c_m}{b_{m+1}}\prod_{i=1}^m (I-r_{1i}LC^{-1})+\tau
\prod_{j=1}^{m+1} (I-r_{2j}LC^{-1})\right)CU^{k+1} =
\dfrac{c_m}{b_{m+1}}\prod_{i=1}^m (I-r_{1i}LC^{-1})\cdot CU^k
\end{equation}\]
We need to compute the roots of the polynomial \[\begin{equation}
R(x) = \dfrac{c_m}{b_{m+1}} \prod_{i=1}^m (x-r_{1i})+\tau
\prod_{j=1}^{m+1} (x-r_{2j})
\end{equation}\] Say \(R\) has
leading coefficient \(\tau\) and \(m+1\) roots \(R_k\) for \(k=1,\ldots,m+1\). That is, \[\begin{equation}
R(x) = \tau\prod_{k=1}^{m+1} (x-R_k)
\end{equation}\] We can then write our scheme as \[\begin{equation}
\left(\tau\prod_{k=1}^{m+1} (I-R_kLC^{-1})\right)CU^{k+1} =
\dfrac{c_m}{b_{m+1}}\prod_{i=1}^m (I-r_{1i}LC^{-1})\cdot CU^k
\end{equation}\] That is, \[\begin{equation}
U^{k+1} = \dfrac{c_m}{b_{m+1}}\dfrac{1}{\tau}C^{-1}\prod_{k=1}^{m+1}
(I-R_kLC^{-1})^{-1}\prod_{i=1}^m (I-r_{1i}LC^{-1})\cdot CU^k
\end{equation}\]
{r}
LHS <- t(Pr)%*% C + time_step * t(Pl)
# Initialize U matrix to store solution at each time step
U_approx <- matrix(NA, nrow = nrow(C), ncol = length(time_seq))
U_approx[, 1] <- U_0
# Time-stepping loop
for (k in 1:(length(time_seq) - 1)) {
# Compute the right-hand side for the second equation
RHS <- t(Pr)%*% C %*% U_approx[, k]
U_approx[, k + 1] <- as.matrix(solve(LHS, RHS))
}
x <- order_to_plot(x)
y <- order_to_plot(y)
max_error_at_each_time <- apply((U_true - U_approx)^2, 2, mean)
U_true <- apply(U_true, 2, order_to_plot)
U_approx <- apply(U_approx, 2, order_to_plot)
# Create interactive plot
fig <- plot_ly()
# Add second line (max_error_at_each_time)
fig <- fig %>% add_trace(
x = ~time_seq, y = ~max_error_at_each_time, type = 'scatter', mode = 'lines+markers',
line = list(color = 'red', width = 2),
marker = list(size = 4),
name = "Max Error True and Approx 2"
)
# Layout
fig <- fig %>% layout(
title = "Max Error at Each Time Step",
xaxis = list(title = "Time"),
yaxis = list(title = "Max Error"),
legend = list(x = 0.1, y = 0.9)
)
plot_data <- data.frame(
x = rep(x, times = ncol(U_true)),
y = rep(y, times = ncol(U_true)),
z_true = as.vector(U_true),
z_approx = as.vector(U_approx),
frame = rep(time_seq, each = length(x))
)
# Compute axis limits
x_range <- range(x)
y_range <- range(y)
z_range <- range(c(U_true, U_approx))
# Initial plot setup (first frame only)
p <- plot_ly(plot_data, frame = ~frame) %>%
add_trace(
x = ~x, y = ~y, z = ~z_true,
type = "scatter3d", mode = "lines",
name = "True",
line = list(color = "blue", width = 2)
) %>%
add_trace(
x = ~x, y = ~y, z = ~z_approx,
type = "scatter3d", mode = "lines",
name = "Approx",
line = list(color = "red", width = 2)
) %>%
layout(
scene = list(
xaxis = list(title = "x", range = x_range),
yaxis = list(title = "y", range = y_range),
zaxis = list(title = "Value", range = z_range),
aspectratio = list(x = 2.4, y = 1.2, z = 1.2),
camera = list(
eye = list(x = 1.5, y = 1.5, z = 1), # Adjust the viewpoint
center = list(x = 0, y = 0, z = 0))
),
updatemenus = list(
list(
type = "buttons", showactive = FALSE,
buttons = list(
list(label = "Play", method = "animate",
args = list(NULL, list(frame = list(duration = 100, redraw = TRUE), fromcurrent = TRUE))),
list(label = "Pause", method = "animate",
args = list(NULL, list(mode = "immediate", frame = list(duration = 0), redraw = FALSE)))
)
)
),
title = "Time: 0"
)
# Convert to plotly object with frame info
pb <- plotly_build(p)
# Inject custom titles into each frame
for (i in seq_along(pb$x$frames)) {
t <- time_seq[i]
err <- signif(max_error_at_each_time[i], 4)
pb$x$frames[[i]]$layout <- list(title = paste0("Time: ", t, " | Max Error: ", err))
}
LS0tCnRpdGxlOiAiU29sdmluZyBhIHBhcmFib2xpYyBlcXVhdGlvbiIKZGF0ZTogIkNyZWF0ZWQ6IDIwLTA0LTIwMjUuIExhc3QgbW9kaWZpZWQ6IGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQtJW0tJVkuJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIG1hdGhqYXg6ICJodHRwczovL2Nkbi5qc2RlbGl2ci5uZXQvbnBtL21hdGhqYXhAMy9lczUvdGV4LW1tbC1jaHRtbC5qcyIKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiBmbGF0bHkKICAgIGNvZGVfZm9sZGluZzogc2hvdyAjIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiIHRvIGhpZGUgY29kZSBhbmQgYWRkIGEgYnV0dG9uIHRvIHNob3cgaXQKICAgIGRmX3ByaW50OiBwYWdlZAogICAgIyB0b2M6IHRydWUKICAgICMgdG9jX2Zsb2F0OgogICAgIyAgIGNvbGxhcHNlZDogdHJ1ZQogICAgIyAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmFsd2F5c19hbGxvd19odG1sOiB0cnVlCmJpYmxpb2dyYXBoeTogCiAgLSByZWZlcmVuY2VzLmJpYgogIC0gZ3JhdGVmdWwtcmVmcy5iaWIKaGVhZGVyLWluY2x1ZGVzOgogIC0gXG5ld2NvbW1hbmR7XGFyfXtcbWF0aGJie1J9fQogIC0gXG5ld2NvbW1hbmR7XGxsYXZ9WzFde1xsZWZ0XHsjMVxyaWdodFx9fQogIC0gXG5ld2NvbW1hbmR7XHBhcmV9WzFde1xsZWZ0KCMxXHJpZ2h0KX0KICAtIFxuZXdjb21tYW5ke1xOY2FsfXtcbWF0aGNhbHtOfX0KICAtIFxuZXdjb21tYW5ke1xWY2FsfXtcbWF0aGNhbHtWfX0KICAtIFxuZXdjb21tYW5ke1xFY2FsfXtcbWF0aGNhbHtFfX0KICAtIFxuZXdjb21tYW5ke1xXY2FsfXtcbWF0aGNhbHtXfX0KLS0tCgpgYGB7ciB4YXJpbmdhbkV4dHJhLWNsaXBib2FyZCwgZWNobyA9IEZBTFNFfQpodG1sdG9vbHM6OnRhZ0xpc3QoCiAgeGFyaW5nYW5FeHRyYTo6dXNlX2NsaXBib2FyZCgKICAgIGJ1dHRvbl90ZXh0ID0gIjxpIGNsYXNzPVwiZmEtc29saWQgZmEtY2xpcGJvYXJkXCIgc3R5bGU9XCJjb2xvcjogIzAwMDA4QlwiPjwvaT4iLAogICAgc3VjY2Vzc190ZXh0ID0gIjxpIGNsYXNzPVwiZmEgZmEtY2hlY2tcIiBzdHlsZT1cImNvbG9yOiAjOTBCRTZEXCI+PC9pPiIsCiAgICBlcnJvcl90ZXh0ID0gIjxpIGNsYXNzPVwiZmEgZmEtdGltZXMtY2lyY2xlXCIgc3R5bGU9XCJjb2xvcjogI0Y5NDE0NFwiPjwvaT4iCiAgKSwKICBybWFya2Rvd246Omh0bWxfZGVwZW5kZW5jeV9mb250X2F3ZXNvbWUoKQopCmBgYAoKCmBgYHtjc3MsIGVjaG8gPSBGQUxTRX0KYm9keSAubWFpbi1jb250YWluZXIgewogIG1heC13aWR0aDogMTAwJSAhaW1wb3J0YW50OwogIHdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7Cn0KYm9keSB7CiAgbWF4LXdpZHRoOiAxMDAlICFpbXBvcnRhbnQ7Cn0KCmJvZHksIHRkIHsKICAgZm9udC1zaXplOiAxNnB4Owp9CmNvZGUucnsKICBmb250LXNpemU6IDE0cHg7Cn0KcHJlIHsKICBmb250LXNpemU6IDE0cHgKfQouY3VzdG9tLWJveCB7CiAgYmFja2dyb3VuZC1jb2xvcjogI2Y1ZjdmYTsgLyogTGlnaHQgZ3JleS1ibHVlIGJhY2tncm91bmQgKi8KICBib3JkZXItY29sb3I6ICNlMWU4ZWQ7IC8qIExpZ2h0IGJvcmRlciBjb2xvciAqLwogIGNvbG9yOiAjMmMzZTUwOyAvKiBEYXJrIHRleHQgY29sb3IgKi8KICBwYWRkaW5nOiAxNXB4OyAvKiBQYWRkaW5nIGluc2lkZSB0aGUgYm94ICovCiAgYm9yZGVyLXJhZGl1czogNXB4OyAvKiBSb3VuZGVkIGNvcm5lcnMgKi8KICBtYXJnaW4tYm90dG9tOiAyMHB4OyAvKiBTcGFjaW5nIGJlbG93IHRoZSBib3ggKi8KfQouY2FwdGlvbiB7CiAgbWFyZ2luOiBhdXRvOwogIHRleHQtYWxpZ246IGNlbnRlcjsKICBtYXJnaW4tYm90dG9tOiAyMHB4OyAvKiBTcGFjaW5nIGJlbG93IHRoZSBib3ggKi8KfQpgYGAKCgpMZXQgdXMgc2V0IHNvbWUgZ2xvYmFsIG9wdGlvbnMgZm9yIGFsbCBjb2RlIGNodW5rcyBpbiB0aGlzIGRvY3VtZW50LgoKCmBgYHtyfQojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTk4MikgCiMgU2V0IGdsb2JhbCBvcHRpb25zIGZvciBhbGwgY29kZSBjaHVua3MKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogICMgRGlzYWJsZSBtZXNzYWdlcyBwcmludGVkIGJ5IFIgY29kZSBjaHVua3MKICBtZXNzYWdlID0gVFJVRSwgICAgCiAgIyBEaXNhYmxlIHdhcm5pbmdzIHByaW50ZWQgYnkgUiBjb2RlIGNodW5rcwogIHdhcm5pbmcgPSBUUlVFLCAgICAKICAjIFNob3cgUiBjb2RlIHdpdGhpbiBjb2RlIGNodW5rcyBpbiBvdXRwdXQKICBlY2hvID0gVFJVRSwgICAgICAgIAogICMgSW5jbHVkZSBib3RoIFIgY29kZSBhbmQgaXRzIHJlc3VsdHMgaW4gb3V0cHV0CiAgaW5jbHVkZSA9IFRSVUUsICAgICAKICAjIEV2YWx1YXRlIFIgY29kZSBjaHVua3MKICBldmFsID0gVFJVRSwgICAgICAgCiAgIyBFbmFibGUgY2FjaGluZyBvZiBSIGNvZGUgY2h1bmtzIGZvciBmYXN0ZXIgcmVuZGVyaW5nCiAgY2FjaGUgPSBGQUxTRSwgICAgICAKICAjIEFsaWduIGZpZ3VyZXMgaW4gdGhlIGNlbnRlciBvZiB0aGUgb3V0cHV0CiAgZmlnLmFsaWduID0gImNlbnRlciIsCiAgIyBFbmFibGUgcmV0aW5hIGRpc3BsYXkgZm9yIGhpZ2gtcmVzb2x1dGlvbiBmaWd1cmVzCiAgcmV0aW5hID0gMiwKICAjIFNob3cgZXJyb3JzIGluIHRoZSBvdXRwdXQgaW5zdGVhZCBvZiBzdG9wcGluZyByZW5kZXJpbmcKICBlcnJvciA9IFRSVUUsCiAgIyBEbyBub3QgY29sbGFwc2UgY29kZSBhbmQgb3V0cHV0IGludG8gYSBzaW5nbGUgYmxvY2sKICBjb2xsYXBzZSA9IEZBTFNFCikKIyBTdGFydCB0aGUgZmlndXJlIGNvdW50ZXIKZmlnX2NvdW50IDwtIDAKIyBEZWZpbmUgdGhlIGNhcHRpb25lciBmdW5jdGlvbgpjYXB0aW9uZXIgPC0gZnVuY3Rpb24oY2FwdGlvbikgewogIGZpZ19jb3VudCA8PC0gZmlnX2NvdW50ICsgMQogIHBhc3RlMCgiRmlndXJlICIsIGZpZ19jb3VudCwgIjogIiwgY2FwdGlvbikKfQojIERlZmluZSB0aGUgZnVuY3Rpb24gdG8gdHJ1bmNhdGUgYSBudW1iZXIgdG8gdHdvIGRlY2ltYWwgcGxhY2VzCnRydW5jYXRlX3RvX3R3byA8LSBmdW5jdGlvbih4KSB7CiAgZmxvb3IoeCAqIDEwMCkgLyAxMDAKfQpgYGAKCmBgYHtyfQpteS5nZXQucm9vdHMgPC0gZnVuY3Rpb24ob3JkZXIsIGJldGEpIHsKICBtdCA8LSBnZXQocGFzdGUwKCJtIiwgb3JkZXIsICJ0YWJsZSIpKQogIHJiIDwtIHJlcCgwLCBvcmRlciArIDEpCiAgcmMgPC0gcmVwKDAsIG9yZGVyKQogIGlmKG9yZGVyID09IDEpIHsKICAgICAgcmMgPSBhcHByb3gobXQkYmV0YSwgbXRbW3Bhc3RlMCgicmMiKV1dLCBiZXRhKSR5CiAgICB9IGVsc2UgewogICAgICByYyA9IHNhcHBseSgxOm9yZGVyLCBmdW5jdGlvbihpKSB7CiAgICAgICAgYXBwcm94KG10JGJldGEsIG10W1twYXN0ZTAoInJjLiIsIGkpXV0sIGJldGEpJHkKICAgICAgfSkKICAgIH0KICAgIHJiID0gc2FwcGx5KDE6KG9yZGVyKzEpLCBmdW5jdGlvbihpKSB7CiAgICAgIGFwcHJveChtdCRiZXRhLCBtdFtbcGFzdGUwKCJyYi4iLCBpKV1dLCB4b3V0ID0gYmV0YSkkeQogICAgfSkKICAgIGZhY3RvciA9IGFwcHJveChtdCRiZXRhLCBtdCRmYWN0b3IsIHhvdXQgPSBiZXRhKSR5CiAgcmV0dXJuKGxpc3QocmIgPSByYiwgcmMgPSByYywgZmFjdG9yID0gZmFjdG9yKSkKfQoKbTF0YWJsZSA8LSByU1BERTo6Om0xdGFibGUKbTJ0YWJsZSA8LSByU1BERTo6Om0ydGFibGUKbTN0YWJsZSA8LSByU1BERTo6Om0zdGFibGUKbTR0YWJsZSA8LSByU1BERTo6Om00dGFibGUKCnBvbHlfZnJvbV9yb290cyA8LSBmdW5jdGlvbihyb290cykgewogIGNvZWYgPC0gMQogIGZvciAociBpbiByb290cykgewogICAgY29lZiA8LSBjb252b2x2ZShjb2VmLCBjKDEsIC1yKSwgdHlwZSA9ICJvcGVuIikKICB9CiAgcmV0dXJuKHJldihjb2VmKSkKfQoKY29tcHV0ZV9zdW1fcG9seSA8LSBmdW5jdGlvbihmYWN0b3IsIHByX3Jvb3RzLCBwbF9yb290cywgY3RlKSB7CiAgCiAgIyBDb2VmZmljaWVudHMgb2YgcCh4KSBhbmQgcSh4KQogIHByX2NvZWYgPC0gcG9seV9mcm9tX3Jvb3RzKHByX3Jvb3RzKQogIHBsX2NvZWYgPC0gcG9seV9mcm9tX3Jvb3RzKHBsX3Jvb3RzKQogIAogICMgUGFkIHRvIGVxdWFsIGxlbmd0aAogIG1heF9sZW4gPC0gbWF4KGxlbmd0aChwcl9jb2VmKSwgbGVuZ3RoKHBsX2NvZWYpKQogIHByX2NvZWYgPC0gYyhyZXAoMCwgbWF4X2xlbiAtIGxlbmd0aChwcl9jb2VmKSksIHByX2NvZWYpCiAgcGxfY29lZiA8LSBjKHJlcCgwLCBtYXhfbGVuIC0gbGVuZ3RoKHBsX2NvZWYpKSwgcGxfY29lZikKICAKICAjIENvbXB1dGUgcih4KQogIHByX3BsdXNfcGxfY29lZiA8LSBmYWN0b3IgKiBwcl9jb2VmICsgY3RlICogcGxfY29lZgogICMgUm9vdHMgb2Ygcih4KQogIHByX3BsdXNfcGxfcm9vdHMgPC0gcG9seXJvb3QocmV2KHByX3BsdXNfcGxfY29lZikpCgogIHJldHVybihsaXN0KG5ld19mYWN0b3IgPSBwcl9wbHVzX3BsX2NvZWZbMV0sIHByX3BsdXNfcGxfcm9vdHMgPSBSZShwcl9wbHVzX3BsX3Jvb3RzKSkpCn0KCm15LmZyYWN0aW9uYWwub3BlcmF0b3JzIDwtIGZ1bmN0aW9uKEwsICMga2FwcGFeMkMgKyBHCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJldGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLmZhY3RvciwgIyBrYXBwYV4yCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG0gPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aW1lX3N0ZXApIHsKCiAgQyA8LSBNYXRyaXg6OkRpYWdvbmFsKGRpbShDKVsxXSwgcm93U3VtcyhDKSkgIyBsdW1wZWQKICBDaSA8LSBNYXRyaXg6OkRpYWdvbmFsKGRpbShDKVsxXSwgMSAvIHJvd1N1bXMoQykpICMgbHVtcGVkIAogIEkgPC0gTWF0cml4OjpEaWFnb25hbChkaW0oQylbMV0pCiAgTCA8LSBMIC8gc2NhbGUuZmFjdG9yICMgQyArIEcva2FwcGFeMgogIExDaSA8LSBMICUqJSBDaQogIHJvb3RzIDwtIG15LmdldC5yb290cyhtLCBiZXRhKQogIFBsLnJvb3RzIDwtIHJvb3RzJHJiCiAgUHIucm9vdHMgPC0gcm9vdHMkcmMKICBmYWN0b3IgPC0gcm9vdHMkZmFjdG9yCiAgbmV3X2ZhY3Rvcl9hbmRfcm9vdHMgPC0gY29tcHV0ZV9zdW1fcG9seShmYWN0b3IsIFByLnJvb3RzLCBQbC5yb290cywgdGltZV9zdGVwKQogIAogIG5ld19yb290cyA8LSBuZXdfZmFjdG9yX2FuZF9yb290cyRwcl9wbHVzX3BsX3Jvb3RzCiAgbmV3X2ZhY3RvciA8LSBuZXdfZmFjdG9yX2FuZF9yb290cyRuZXdfZmFjdG9yCiAgCiAgUHJfcGx1c19QbC5mYWN0b3JzIDwtIGxpc3QoKQogIFByX3BsdXNfUGwuZmFjdG9yc1tbMV1dIDwtIEkgLSBMQ2kgKiBuZXdfcm9vdHNbMV0KICAKICBpZiAobGVuZ3RoKG5ld19yb290cykgPiAxKSB7CiAgICBmb3IgKGkgaW4gMjpsZW5ndGgobmV3X3Jvb3RzKSkgewogICAgICBQcl9wbHVzX1BsLmZhY3RvcnNbW2ldXSA8LSBJIC0gTENpICogbmV3X3Jvb3RzW2ldCiAgICB9CiAgfQogIFByLmZhY3RvcnMgPC0gbGlzdCgpCiAgUHIuZmFjdG9yc1tbMV1dIDwtIEkgLSBMQ2kgKiByb290cyRyY1sxXQoKICBpZiAobGVuZ3RoKHJvb3RzJHJjKSA+IDEpIHsKICAgIGZvciAoaSBpbiAyOmxlbmd0aChyb290cyRyYykpIHsKICAgICAgUHIuZmFjdG9yc1tbaV1dIDwtIEkgLSBMQ2kgKiByb290cyRyY1tpXQogICAgfQogIH0KICAKICAjUHIuZmFjdG9yc1tbbGVuZ3RoKFByLmZhY3RvcnMpICsgMV1dIDwtIEkKICAgCiAgb3V0cHV0IDwtIGxpc3QoCiAgICBDaSA9IENpLAogICAgQyA9IEMsCiAgICBMQ2kgPSBMQ2ksCiAgICBMID0gTCwKICAgIG0gPSBtLAogICAgYmV0YSA9IGJldGEsCiAgICBmYWN0b3IgPSBmYWN0b3IsCiAgICBuZXdfZmFjdG9yID0gbmV3X2ZhY3RvciwKICAgIFByLmZhY3RvcnMgPSBQci5mYWN0b3JzLAogICAgUHJfcGx1c19QbC5mYWN0b3JzID0gUHJfcGx1c19QbC5mYWN0b3JzCiAgKQogIHJldHVybihvdXRwdXQpCn0KYGBgCgoKCgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJJTkxBIixyZXBvcz1jKGdldE9wdGlvbigicmVwb3MiKSxJTkxBPSJodHRwczovL2lubGEuci1pbmxhLWRvd25sb2FkLm9yZy9SL3Rlc3RpbmciKSwgZGVwPVRSVUUpCiMgaW5sYS51cGdyYWRlKHRlc3RpbmcgPSBUUlVFKQojIHJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJpbmxhYnJ1LW9yZy9pbmxhYnJ1IiwgcmVmID0gImRldmVsIikKIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1YigiZGF2aWRib2xpbi9yc3BkZSIsIHJlZiA9ICJkZXZlbCIpCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImRhdmlkYm9saW4vbWV0cmljZ3JhcGgiLCByZWYgPSAiZGV2ZWwiKQpsaWJyYXJ5KElOTEEpCmxpYnJhcnkoaW5sYWJydSkKbGlicmFyeShyU1BERSkKbGlicmFyeShNZXRyaWNHcmFwaCkKbGlicmFyeShncmF0ZWZ1bCkKCmxpYnJhcnkocGxvdGx5KQpgYGAKCgpXZSB3YW50IHRvIHNvbHZlIHRoZSBmcmFjdGlvbmFsIGRpZmZ1c2lvbiBlcXVhdGlvbgpcYmVnaW57ZXF1YXRpb259ClxsYWJlbHtlcTptYWluZXF9CiAgICBccGFydGlhbF90IHUrKFxrYXBwYV4yLVxEZWx0YV9cR2FtbWEpXntcZnJhY3tcYWxwaGF9ezJ9fSB1PWYgXHRleHQgeyBvbiB9IFxHYW1tYSBcdGltZXMoMCwgVCksIFxxdWFkIHUoMCk9dV8wIFx0ZXh0IHsgb24gfSBcR2FtbWEsClxlbmR7ZXF1YXRpb259CndoZXJlICR1JCBzYXRpc2ZpZXMgdGhlIEtpcmNoaG9mZiB2ZXJ0ZXggY29uZGl0aW9ucwpcYmVnaW57ZXF1YXRpb259ClxsYWJlbHtlcTpLY29uZH0KICAgIFxsZWZ0XHtccGhpXGluIEMoXEdhbW1hKVw7XEJpZ3xcOyBcZm9yYWxsIHZcaW4gVjogXHN1bV97ZVxpblxtYXRoY2Fse0V9X3Z9XHBhcnRpYWxfZSBccGhpKHYpPTAgXHJpZ2h0XH0KXGVuZHtlcXVhdGlvbn0KCklmICRmPTAkLCB0aGVuIHRoZSBzb2x1dGlvbiBpcyBnaXZlbiBieQpcYmVnaW57ZXF1YXRpb259ClxsYWJlbHtlcTpzb2xfcmVwcmVudGF0aW9ufQogICAgICAgIHUocyx0KSA9IFxkaXNwbGF5c3R5bGVcc3VtX3tqXGluXG1hdGhiYntOfX1lXnstXGxhbWJkYV57XGZyYWN7XGFscGhhfXsyfX1fanR9XGxlZnQodV8wLCBlX2pccmlnaHQpX3tMXzIoXEdhbW1hKX1lX2oocykuClxlbmR7ZXF1YXRpb259CgpgYGB7cn0KIyBGdW5jdGlvbiB0byBidWlsZCBhIHRhZHBvbGUgZ3JhcGggYW5kIGNyZWF0ZSBhIG1lc2gKZ2V0c19ncmFwaF90YWRwb2xlIDwtIGZ1bmN0aW9uKGgpewogIGVkZ2UxIDwtIHJiaW5kKGMoMCwwKSxjKDEsMCkpCiAgdGhldGEgPC0gc2VxKGZyb209LXBpLHRvPXBpLGxlbmd0aC5vdXQgPSAxMDApCiAgZWRnZTIgPC0gY2JpbmQoMSsxL3BpK2Nvcyh0aGV0YSkvcGksc2luKHRoZXRhKS9waSkKICBlZGdlcyA9IGxpc3QoZWRnZTEsIGVkZ2UyKQogIGdyYXBoIDwtIG1ldHJpY19ncmFwaCRuZXcoZWRnZXMgPSBlZGdlcykKICBncmFwaCRidWlsZF9tZXNoKGggPSBoKQogIHJldHVybihncmFwaCkKfQpgYGAKCkxldCAkXEdhbW1hX1QgPSAoXFZjYWwsXEVjYWwpJCBjaGFyYWN0ZXJpemUgdGhlIHRhZHBvbGUgZ3JhcGggd2l0aCAkXFZjYWwgPSBce3ZfMSx2XzJcfSQgYW5kICRcRWNhbCA9IFx7ZV8xLGVfMlx9JC4gVGhlIGxlZnQgZWRnZSAkZV8xJCBoYXMgbGVuZ3RoIDEgYW5kIHRoZSBjaXJjdWxhciBlZGdlICRlXzIkIGhhcyBsZW5ndGggMi4gQXMgZGlzY3Vzc2VkIGJlZm9yZSwgYSBwb2ludCBvbiAkZV8xJCBpcyBwYXJhbWV0ZXJpemVkIHZpYSAkcz1cbGVmdChlXzEsIHRccmlnaHQpJCBmb3IgJHQgXGluWzAsMV0kIGFuZCBhIHBvaW50IG9uICRlXzIkIHZpYSAkcz1cbGVmdChlXzIsIHRccmlnaHQpJCBmb3IgJHRcaW5bMCwyXSQuIE9uZSBjYW4gdmVyaWZ5IHRoYXQgJC1cRGVsdGFfXEdhbW1hJCBoYXMgZWlnZW52YWx1ZXMgJDAsXGxlZnRceyhpIFxwaSAvIDIpXjJccmlnaHRcfV97aSBcaW4gXG1hdGhiYntOfX0kIGFuZCAkXGxlZnRceyhpIFxwaSAvIDIpXjJccmlnaHRcfV97MiBpIFxpbiBcbWF0aGJie059fSQgd2l0aCBjb3JyZXNwb25kaW5nIGVpZ2VuZnVuY3Rpb25zICRccGhpXzAkLCAkXGxlZnRce1xwaGlfaVxyaWdodFx9X3tpIFxpbiBcbWF0aGJie059fSQsIGFuZCAkXGxlZnRce1xwc2lfaVxyaWdodFx9X3syIGkgXGluIFxtYXRoYmJ7Tn19JCBnaXZlbiBieSAkXHBoaV8wKHMpPTEgLyBcc3FydHszfSQgYW5kIApcYmVnaW57ZXF1YXRpb24qfQogICAgXHBoaV9pKHMpPUNfe1xwaGksIGl9XGJlZ2lue2Nhc2VzfQogICAgICAgIC0yIFxzaW4gKFxmcmFje2lccGl9ezJ9KSBcY29zIChcZnJhY3tpIFxwaSB0fXsyfSksICYgcyBcaW4gZV8xLCBcXApcc2luIChpIFxwaSB0IC8gMiksICYgcyBcaW4gZV8yLAogICAgXGVuZHtjYXNlc30sClxxdWFkIAogICAgXHBzaV9pKHMpPVxmcmFje1xzcXJ0ezN9fXtcc3FydHsyfX0gXGJlZ2lue2Nhc2VzfQogICAgKC0xKV57aSAvIDJ9IFxjb3MgKFxmcmFje2kgXHBpIHR9ezJ9KSwgJiBzIFxpbiBlXzEsIFxcClxjb3MgKFxmcmFje2kgXHBpIHR9ezJ9KSwgJiBzIFxpbiBlXzIsClxlbmR7Y2FzZXN9LApcZW5ke2VxdWF0aW9uKn0Kd2hlcmUgJENfe1xwaGksIGl9PTEkIGlmICRpJCBpcyBldmVuIGFuZCAkQ197XHBoaSwgaX09MSAvIFxzcXJ0ezN9JCBvdGhlcndpc2UuIE1vcmVvdmVyLCB0aGVzZSBmdW5jdGlvbnMgZm9ybSBhbiBvcnRob25vcm1hbCBiYXNpcyBmb3IgJExfMihcR2FtbWFfVCkkLgoKYGBge3J9CiMgRnVuY3Rpb24gdG8gY29tcHV0ZSB0aGUgZWlnZW5mdW5jdGlvbnMgCnRhZHBvbGUuZWlnIDwtIGZ1bmN0aW9uKGssZ3JhcGgpewp4MSA8LSBjKDAsZ3JhcGgkZ2V0X2VkZ2VfbGVuZ3RocygpWzFdKmdyYXBoJG1lc2gkUHRFW2dyYXBoJG1lc2gkUHRFWywxXT09MSwyXSkgCngyIDwtIGMoMCxncmFwaCRnZXRfZWRnZV9sZW5ndGhzKClbMl0qZ3JhcGgkbWVzaCRQdEVbZ3JhcGgkbWVzaCRQdEVbLDFdPT0yLDJdKSAKCmlmKGs9PTApeyAKICBmLmUxIDwtIHJlcCgxLGxlbmd0aCh4MSkpIAogIGYuZTIgPC0gcmVwKDEsbGVuZ3RoKHgyKSkgCiAgZjEgPSBjKGYuZTFbMV0sZi5lMlsxXSxmLmUxWy0xXSwgZi5lMlstMV0pIAogIGYgPSBsaXN0KHBoaT1mMS9zcXJ0KDMpKSAKICAKfSBlbHNlIHsKICBmLmUxIDwtIC0yKnNpbihwaSprKjEvMikqY29zKHBpKmsqeDEvMikgCiAgZi5lMiA8LSBzaW4ocGkqayp4Mi8yKSAgICAgICAgICAgICAgICAgIAogIAogIGYxID0gYyhmLmUxWzFdLGYuZTJbMV0sZi5lMVstMV0sIGYuZTJbLTFdKSAKICAKICBpZigoayAlJSAyKT09MSl7IAogICAgZiA9IGxpc3QocGhpPWYxL3NxcnQoMykpIAogIH0gZWxzZSB7IAogICAgZi5lMSA8LSAoLTEpXntrLzJ9KmNvcyhwaSprKngxLzIpCiAgICBmLmUyIDwtIGNvcyhwaSprKngyLzIpCiAgICBmMiA9IGMoZi5lMVsxXSxmLmUyWzFdLGYuZTFbLTFdLGYuZTJbLTFdKSAKICAgIGYgPC0gbGlzdChwaGk9ZjEscHNpPWYyL3NxcnQoMy8yKSkKICB9Cn0KCnJldHVybihmKQp9CmBgYAoKSW1wbGVtZW50YXRpb24gb2YgJHUkCgpgYGB7cn0KaCA8LSAwLjAxCmdyYXBoIDwtIGdldHNfZ3JhcGhfdGFkcG9sZShoID0gaCkKVF9maW5hbCA8LSAyCnRpbWVfc3RlcCA8LSAwLjAxCnRpbWVfc2VxIDwtIHNlcSgwLCBUX2ZpbmFsLCBieSA9IHRpbWVfc3RlcCkKIyBDb21wdXRlIHRoZSBGRU0gbWF0cmljZXMKZ3JhcGgkY29tcHV0ZV9mZW0oKQpHIDwtIGdyYXBoJG1lc2gkRwpDIDwtIGdyYXBoJG1lc2gkQwpJIDwtIE1hdHJpeDo6RGlhZ29uYWwobnJvdyhDKSkKeCA8LSBncmFwaCRtZXNoJFZbLCAxXQp5IDwtIGdyYXBoJG1lc2gkVlssIDJdCmVkZ2VfbnVtYmVyIDwtIGdyYXBoJG1lc2gkVnRFWywgMV0KcG9zIDwtIHN1bShlZGdlX251bWJlciA9PSAxKSsxCm9yZGVyX3RvX3Bsb3QgPC0gZnVuY3Rpb24odilyZXR1cm4oYyh2WzFdLCB2WzM6cG9zXSwgdlsyXSwgdlsocG9zKzEpOmxlbmd0aCh2KV0sIHZbMl0pKQp3ZWlnaHRzIDwtIGdyYXBoJG1lc2gkd2VpZ2h0cwojIEluaXRpYWwgY29uZGl0aW9uCiMgVV8wIDwtIDEwKmV4cCgtKCh4LTEpXjIgKyAoeSleMikpCmBgYAoKCmBgYHtyfQprYXBwYSA8LSAxCmFscGhhIDwtIDAuNSAjIGZyb20gMC41IHRvIDIKbSA9IDIKYmV0YSA8LSBhbHBoYS8yCkwgPC0ga2FwcGFeMipDICsgRwpgYGAKCgpgYGB7cn0KbXlfb3AgPC0gbXkuZnJhY3Rpb25hbC5vcGVyYXRvcnMoTCwgYmV0YSwgQywgc2NhbGUuZmFjdG9yID0ga2FwcGFeMiwgbSA9IG0sIHRpbWVfc3RlcCkKCm15LnNvbHZlciA8LSBmdW5jdGlvbihvYmosIHYpewogIFByLmZhY3RvcnMgPC0gb2JqJFByLmZhY3RvcnMKICBQcl9wbHVzX1BsLmZhY3RvcnMgPC0gb2JqJFByX3BsdXNfUGwuZmFjdG9ycwogIG0gPC0gb2JqJG0KICBDIDwtIG9iaiRDCiAgQ2kgPC0gb2JqJENpCiAgZmFjdG9yIDwtIG9iaiRmYWN0b3IKICBuZXdfZmFjdG9yIDwtIG9iaiRuZXdfZmFjdG9yCiAgaWYgKG09PTEpewogICAgdGVtcCA8LSBzb2x2ZShQcl9wbHVzX1BsLmZhY3RvcnNbWzJdXSwgCiAgICAgICAgICAgICAgICAgIFByLmZhY3RvcnNbWzFdXSAlKiUgc29sdmUoCiAgICAgICAgICAgICAgICAgICAgUHJfcGx1c19QbC5mYWN0b3JzW1sxXV0sIAogICAgICAgICAgICAgICAgICAgIEMlKiV2KSkKICB9IGVsc2UgaWYgKG09PTIpewogICAgdGVtcCA8LSBzb2x2ZShQcl9wbHVzX1BsLmZhY3RvcnNbWzNdXSwgCiAgICAgICAgICAgICAgICAgIFByLmZhY3RvcnNbWzJdXSAlKiUgc29sdmUoCiAgICAgICAgICAgICAgICAgICAgUHJfcGx1c19QbC5mYWN0b3JzW1syXV0sIAogICAgICAgICAgICAgICAgICAgIFByLmZhY3RvcnNbWzFdXSAlKiUgc29sdmUoCiAgICAgICAgICAgICAgICAgICAgICBQcl9wbHVzX1BsLmZhY3RvcnNbWzFdXSwgCiAgICAgICAgICAgICAgICAgICAgICBDJSoldikpKQogIH0gZWxzZSBpZiAobT09Myl7CiAgICB0ZW1wIDwtIHNvbHZlKFByX3BsdXNfUGwuZmFjdG9yc1tbNF1dLCAKICAgICAgICAgICAgICAgICAgUHIuZmFjdG9yc1tbM11dICUqJSBzb2x2ZSgKICAgICAgICAgICAgICAgICAgICBQcl9wbHVzX1BsLmZhY3RvcnNbWzNdXSwgCiAgICAgICAgICAgICAgICAgICAgUHIuZmFjdG9yc1tbMl1dICUqJSBzb2x2ZSgKICAgICAgICAgICAgICAgICAgICAgIFByX3BsdXNfUGwuZmFjdG9yc1tbMl1dLCAKICAgICAgICAgICAgICAgICAgICAgIFByLmZhY3RvcnNbWzFdXSAlKiUgc29sdmUoCiAgICAgICAgICAgICAgICAgICAgICAgIFByX3BsdXNfUGwuZmFjdG9yc1tbMV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgQyUqJXYpKSkpCiAgfSBlbHNlIGlmIChtPT00KXsKICAgIHRlbXAgPC0gc29sdmUoUHJfcGx1c19QbC5mYWN0b3JzW1s1XV0sIAogICAgICAgICAgICAgICAgICBQci5mYWN0b3JzW1s0XV0gJSolIHNvbHZlKAogICAgICAgICAgICAgICAgICAgIFByX3BsdXNfUGwuZmFjdG9yc1tbNF1dLCAKICAgICAgICAgICAgICAgICAgICBQci5mYWN0b3JzW1szXV0gJSolIHNvbHZlKAogICAgICAgICAgICAgICAgICAgICAgUHJfcGx1c19QbC5mYWN0b3JzW1szXV0sIAogICAgICAgICAgICAgICAgICAgICAgUHIuZmFjdG9yc1tbMl1dICUqJSBzb2x2ZSgKICAgICAgICAgICAgICAgICAgICAgICAgUHJfcGx1c19QbC5mYWN0b3JzW1syXV0sIAogICAgICAgICAgICAgICAgICAgICAgICBQci5mYWN0b3JzW1sxXV0gJSolIHNvbHZlKAogICAgICAgICAgICAgICAgICAgICAgICAgIFByX3BsdXNfUGwuZmFjdG9yc1tbMV1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBDJSoldikpKSkpCiAgfQogIHJldHVybigoMS9mYWN0b3IvbmV3X2ZhY3RvcikgKiBDaSAlKiUgdGVtcCkKfQpgYGAKCgpgYGB7cn0Kb3AgPC0gZnJhY3Rpb25hbC5vcGVyYXRvcnMoTCwgYmV0YSwgQywgc2NhbGUuZmFjdG9yID0ga2FwcGFeMiwgbSA9IG0pClBsIDwtIG9wJFBsClByIDwtIG9wJFByCkNpIDwtIG9wJENpCkMgPC0gb3AkQwpgYGAKCgoKYGBge3J9CiMgUGFyYW1ldGVycyB0byBjb25zdHJ1Y3QgVV8wCk5fZmluaXRlIDwtIDQgIyBjaG9vc2UgYW4gZXZlbiBudW1iZXIKYWRqdXN0ZWRfTl9maW5pdGUgPC0gTl9maW5pdGUgKyBOX2Zpbml0ZS8yICsgMQpFSUdFTlZBTF9BTFBIQSA8LSBOVUxMCkVJR0VORlVOIDwtIE5VTEwgICAgICAgCmZvciAoaiBpbiAwOk5fZmluaXRlKSB7CiAgICBsYW1iZGFfal9hbHBoYSA8LSAoa2FwcGFeMiArIChqKnBpLzIpXjIpXihhbHBoYS8yKQogICAgZV9qIDwtIHRhZHBvbGUuZWlnKGosZ3JhcGgpJHBoaQogICAgRUlHRU5WQUxfQUxQSEEgPC0gYyhFSUdFTlZBTF9BTFBIQSwgbGFtYmRhX2pfYWxwaGEpICAgICAgICAgCiAgICBFSUdFTkZVTiA8LSBjYmluZChFSUdFTkZVTiwgZV9qKSAgICAgICAgICAgIAogICAgaWYgKGo+MCAmJiAoaiAlJSAyID09IDApKSB7CiAgICAgIGxhbWJkYV9qX2FscGhhIDwtIChrYXBwYV4yICsgKGoqcGkvMileMileKGFscGhhLzIpCiAgICAgIGVfaiA8LSB0YWRwb2xlLmVpZyhqLGdyYXBoKSRwc2kKICAgICAgRUlHRU5WQUxfQUxQSEEgPC0gYyhFSUdFTlZBTF9BTFBIQSwgbGFtYmRhX2pfYWxwaGEpICAgICAgICAgCiAgICAgIEVJR0VORlVOIDwtIGNiaW5kKEVJR0VORlVOLCBlX2opICAgICAgICAgICAKICAgIH0KfQoKIyBCdWlsZGluZyB0aGUgaW5pdGlhbCBjb25kaXRpb24gYXMgXHN1bSBjb2VmZl9qIEVJR0VORlVOX2oKY29lZmYgPC0gMSooMTphZGp1c3RlZF9OX2Zpbml0ZSleLTEKY29lZmZbLTVdIDwtIDAKIyBsb3dlcl96ZXJvZXIgPC0gMTAgICMgY2hvb3NlIGFuIGV2ZW4gbnVtYmVyCiMgYWRqdXN0ZWRfbG93ZXJfemVyb2VyIDwtIGxvd2VyX3plcm9lciArIGxvd2VyX3plcm9lci8yICsgMQojIGNvZWZmW2FkanVzdGVkX2xvd2VyX3plcm9lcjphZGp1c3RlZF9OX2Zpbml0ZV0gPC0gMApVXzAgPC0gRUlHRU5GVU4gJSolIGNvZWZmCgojIEJ1aWxkaW5nIHRoZSB0cnVlIHNvbHV0aW9uIGFzIFxzdW0gY29lZmZfaiBFSUdFTkZVTl9qIGVeey1cbGFtYmRhX2pee1xmcmFje1xhbHBoYX17Mn19dH0KVV90cnVlIDwtIG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aCh4KSwgbmNvbCA9IGxlbmd0aCh0aW1lX3NlcSkpCmZvciAoayBpbiAxOmxlbmd0aCh0aW1lX3NlcSkpIHsKICBhdXhfayA8LSByZXAoMCwgbGVuZ3RoKHgpKQogIGZvciAoaiBpbiAxOmFkanVzdGVkX05fZmluaXRlKSB7CiAgICBhdXhfayA8LSBhdXhfayArIGV4cCgtdGltZV9zZXFba10qRUlHRU5WQUxfQUxQSEFbal0pKmNvZWZmW2pdKkVJR0VORlVOWywgal0KICB9CiAgVV90cnVlWywga10gPC0gYXV4X2sKfQpgYGAKCmBgYHtyfQpVX2FwcHJveCA8LSBtYXRyaXgoTkEsIG5yb3cgPSBucm93KEMpLCBuY29sID0gbGVuZ3RoKHRpbWVfc2VxKSkKVV9hcHByb3hbLCAxXSA8LSBVXzAKCiMgVGltZS1zdGVwcGluZyBsb29wCmZvciAoayBpbiAxOihsZW5ndGgodGltZV9zZXEpIC0gMSkpIHsKICBVX2FwcHJveFssIGsgKyAxXSA8LSBhcy5tYXRyaXgobXkuc29sdmVyKG15X29wLCBVX2FwcHJveFssIGtdKSkKfQpgYGAKCmBgYAp7cn0KUHIuYXBwbHkubXVsdCA8LSBmdW5jdGlvbih2KXtyZXR1cm4oUHIubXVsdChvcCwgdikpfQpQbC5hcHBseS5zb2x2ZSA8LSBmdW5jdGlvbih2KXtyZXR1cm4oUGwuc29sdmUob3AsIHYpKX0KUGxpQyA8LSBhcHBseShDLCAyLCBQbC5hcHBseS5zb2x2ZSkgIyBQbENeLTEKIyBQcmVjb21wdXRlIHRoZSBMSFMxIG1hdHJpeAphdXggPC0gYXBwbHkoUGxpQywgMiwgUHIuYXBwbHkubXVsdCkgI1ByUGxeLTFDCkxIUyA8LSBhdXggKyB0aW1lX3N0ZXAgKiBNYXRyaXg6OkRpYWdvbmFsKG5yb3coQykpIAoKIyBJbml0aWFsaXplIFUgbWF0cml4IHRvIHN0b3JlIHNvbHV0aW9uIGF0IGVhY2ggdGltZSBzdGVwClVfYXBwcm94IDwtIG1hdHJpeChOQSwgbnJvdyA9IG5yb3coQyksIG5jb2wgPSBsZW5ndGgodGltZV9zZXEpKQpVX2FwcHJveFssIDFdIDwtIFVfMAoKIyBUaW1lLXN0ZXBwaW5nIGxvb3AKZm9yIChrIGluIDE6KGxlbmd0aCh0aW1lX3NlcSkgLSAxKSkgewogICMgQ29tcHV0ZSB0aGUgcmlnaHQtaGFuZCBzaWRlIGZvciB0aGUgc2Vjb25kIGVxdWF0aW9uCiAgUkhTIDwtIGF1eCAlKiUgVV9hcHByb3hbLCBrXQogIFVfYXBwcm94WywgayArIDFdIDwtIGFzLm1hdHJpeChzb2x2ZShMSFMsIFJIUykpCn0KYGBgCgpXZSBhcnJpdmUgYXQgdGhlIHNjaGVtZQoKXGJlZ2lue2VxdWF0aW9ufQooUF9yXlRDK1x0YXUgUF9cZWxsXlQpVV57aysxfSA9IFBfcl5UQ1VeawpcbGFiZWx7ZXE6c2NoZW1lfQpcZW5ke2VxdWF0aW9ufQp3aGVyZQpcYmVnaW57ZXF1YXRpb259ClBfciA9IGNfbVxwcm9kX3tpPTF9Xm0gKEktcl97MWl9Q157LTF9TClccXVhZFx0ZXh0e2FuZH1ccXVhZCBQX1xlbGwgPSBiX3ttKzF9Q1xwcm9kX3tqPTF9XnttKzF9IChJLXJfezJqfUNeey0xfUwpClxlbmR7ZXF1YXRpb259Ck9ic2VydmUgdGhhdCAKXGJlZ2lue2VxdWF0aW9ufQpQX3JeVCA9IGNfbVxwcm9kX3tpPW19XjEgKEktcl97MWl9TENeey0xfSlccXVhZFx0ZXh0e2FuZH1ccXVhZCBQX1xlbGxeVCA9IGJfe20rMX1ccHJvZF97aj1tKzF9XnsxfSAoSS1yX3syan1MQ157LTF9KVxjZG90IEMKXGVuZHtlcXVhdGlvbn0KUmVwbGFjaW5nIHRoZXNlIHR3byBpbiBvdXIgc2NoZW1lIHdlIGdldApcYmVnaW57ZXF1YXRpb259ClxsZWZ0KGNfbVxwcm9kX3tpPW19XjEgKEktcl97MWl9TENeey0xfSkrXHRhdSBiX3ttKzF9XHByb2Rfe2o9bSsxfV57MX0gKEktcl97Mmp9TENeey0xfSlccmlnaHQpQ1Vee2srMX0gPSBjX21ccHJvZF97aT1tfV4xIChJLXJfezFpfUxDXnstMX0pXGNkb3QgQ1VeawpcZW5ke2VxdWF0aW9ufQpXZSBjYW4gZXF1aXZhbGVudGx5IHdyaXRlIHRoaXMgYXMKXGJlZ2lue2VxdWF0aW9ufQpcbGVmdChcZGZyYWN7Y19tfXtiX3ttKzF9fVxwcm9kX3tpPTF9Xm0gKEktcl97MWl9TENeey0xfSkrXHRhdSBccHJvZF97aj0xfV57bSsxfSAoSS1yX3syan1MQ157LTF9KVxyaWdodClDVV57aysxfSA9IFxkZnJhY3tjX219e2Jfe20rMX19XHByb2Rfe2k9MX1ebSAoSS1yX3sxaX1MQ157LTF9KVxjZG90IENVXmsKXGVuZHtlcXVhdGlvbn0KCldlIG5lZWQgdG8gY29tcHV0ZSB0aGUgcm9vdHMgb2YgdGhlIHBvbHlub21pYWwKXGJlZ2lue2VxdWF0aW9ufQpSKHgpID0gXGRmcmFje2NfbX17Yl97bSsxfX0gXHByb2Rfe2k9MX1ebSAoeC1yX3sxaX0pK1x0YXUgXHByb2Rfe2o9MX1ee20rMX0gKHgtcl97Mmp9KSAKXGVuZHtlcXVhdGlvbn0KU2F5ICRSJCBoYXMgbGVhZGluZyBjb2VmZmljaWVudCAkXHRhdSQgYW5kICRtKzEkIHJvb3RzICRSX2skIGZvciAkaz0xLFxsZG90cyxtKzEkLiBUaGF0IGlzLApcYmVnaW57ZXF1YXRpb259ClIoeCkgPSBcdGF1XHByb2Rfe2s9MX1ee20rMX0gKHgtUl9rKQpcZW5ke2VxdWF0aW9ufQpXZSBjYW4gdGhlbiB3cml0ZSBvdXIgc2NoZW1lIGFzIApcYmVnaW57ZXF1YXRpb259ClxsZWZ0KFx0YXVccHJvZF97az0xfV57bSsxfSAoSS1SX2tMQ157LTF9KVxyaWdodClDVV57aysxfSA9IFxkZnJhY3tjX219e2Jfe20rMX19XHByb2Rfe2k9MX1ebSAoSS1yX3sxaX1MQ157LTF9KVxjZG90IENVXmsKXGVuZHtlcXVhdGlvbn0KVGhhdCBpcywKXGJlZ2lue2VxdWF0aW9ufQpVXntrKzF9ID0gXGRmcmFje2NfbX17Yl97bSsxfX1cZGZyYWN7MX17XHRhdX1DXnstMX1ccHJvZF97az0xfV57bSsxfSAoSS1SX2tMQ157LTF9KV57LTF9XHByb2Rfe2k9MX1ebSAoSS1yX3sxaX1MQ157LTF9KVxjZG90IENVXmsKXGVuZHtlcXVhdGlvbn0KCgpgYGAKe3J9CkxIUyA8LSB0KFByKSUqJSBDICsgdGltZV9zdGVwICogdChQbCkKIyBJbml0aWFsaXplIFUgbWF0cml4IHRvIHN0b3JlIHNvbHV0aW9uIGF0IGVhY2ggdGltZSBzdGVwClVfYXBwcm94IDwtIG1hdHJpeChOQSwgbnJvdyA9IG5yb3coQyksIG5jb2wgPSBsZW5ndGgodGltZV9zZXEpKQpVX2FwcHJveFssIDFdIDwtIFVfMAoKIyBUaW1lLXN0ZXBwaW5nIGxvb3AKZm9yIChrIGluIDE6KGxlbmd0aCh0aW1lX3NlcSkgLSAxKSkgewogICMgQ29tcHV0ZSB0aGUgcmlnaHQtaGFuZCBzaWRlIGZvciB0aGUgc2Vjb25kIGVxdWF0aW9uCiAgUkhTIDwtIHQoUHIpJSolIEMgJSolIFVfYXBwcm94Wywga10KICBVX2FwcHJveFssIGsgKyAxXSA8LSBhcy5tYXRyaXgoc29sdmUoTEhTLCBSSFMpKQp9CmBgYAoKCmBgYHtyfQp4IDwtIG9yZGVyX3RvX3Bsb3QoeCkKeSA8LSBvcmRlcl90b19wbG90KHkpCm1heF9lcnJvcl9hdF9lYWNoX3RpbWUgPC0gYXBwbHkoKFVfdHJ1ZSAtIFVfYXBwcm94KV4yLCAyLCBtZWFuKQoKVV90cnVlIDwtIGFwcGx5KFVfdHJ1ZSwgMiwgb3JkZXJfdG9fcGxvdCkKVV9hcHByb3ggPC0gYXBwbHkoVV9hcHByb3gsIDIsIG9yZGVyX3RvX3Bsb3QpCgoKCiMgQ3JlYXRlIGludGVyYWN0aXZlIHBsb3QKZmlnIDwtIHBsb3RfbHkoKQoKCiMgQWRkIHNlY29uZCBsaW5lIChtYXhfZXJyb3JfYXRfZWFjaF90aW1lKQpmaWcgPC0gZmlnICU+JSBhZGRfdHJhY2UoCiAgeCA9IH50aW1lX3NlcSwgeSA9IH5tYXhfZXJyb3JfYXRfZWFjaF90aW1lLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzK21hcmtlcnMnLAogIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JlZCcsIHdpZHRoID0gMiksCiAgbWFya2VyID0gbGlzdChzaXplID0gNCksCiAgbmFtZSA9ICJNYXggRXJyb3IgVHJ1ZSBhbmQgQXBwcm94IDIiCikKCiMgTGF5b3V0CmZpZyA8LSBmaWcgJT4lIGxheW91dCgKICB0aXRsZSA9ICJNYXggRXJyb3IgYXQgRWFjaCBUaW1lIFN0ZXAiLAogIHhheGlzID0gbGlzdCh0aXRsZSA9ICJUaW1lIiksCiAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIk1heCBFcnJvciIpLAogIGxlZ2VuZCA9IGxpc3QoeCA9IDAuMSwgeSA9IDAuOSkKKQoKCnBsb3RfZGF0YSA8LSBkYXRhLmZyYW1lKAogIHggPSByZXAoeCwgdGltZXMgPSBuY29sKFVfdHJ1ZSkpLAogIHkgPSByZXAoeSwgdGltZXMgPSBuY29sKFVfdHJ1ZSkpLAogIHpfdHJ1ZSA9IGFzLnZlY3RvcihVX3RydWUpLAogIHpfYXBwcm94ID0gYXMudmVjdG9yKFVfYXBwcm94KSwKICBmcmFtZSA9IHJlcCh0aW1lX3NlcSwgZWFjaCA9IGxlbmd0aCh4KSkKKQoKIyBDb21wdXRlIGF4aXMgbGltaXRzCnhfcmFuZ2UgPC0gcmFuZ2UoeCkKeV9yYW5nZSA8LSByYW5nZSh5KQp6X3JhbmdlIDwtIHJhbmdlKGMoVV90cnVlLCBVX2FwcHJveCkpCgojIEluaXRpYWwgcGxvdCBzZXR1cCAoZmlyc3QgZnJhbWUgb25seSkKcCA8LSBwbG90X2x5KHBsb3RfZGF0YSwgZnJhbWUgPSB+ZnJhbWUpICU+JQogIGFkZF90cmFjZSgKICAgIHggPSB+eCwgeSA9IH55LCB6ID0gfnpfdHJ1ZSwKICAgIHR5cGUgPSAic2NhdHRlcjNkIiwgbW9kZSA9ICJsaW5lcyIsCiAgICBuYW1lID0gIlRydWUiLAogICAgbGluZSA9IGxpc3QoY29sb3IgPSAiYmx1ZSIsIHdpZHRoID0gMikKICApICU+JQogIGFkZF90cmFjZSgKICAgIHggPSB+eCwgeSA9IH55LCB6ID0gfnpfYXBwcm94LAogICAgdHlwZSA9ICJzY2F0dGVyM2QiLCBtb2RlID0gImxpbmVzIiwKICAgIG5hbWUgPSAiQXBwcm94IiwKICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIsIHdpZHRoID0gMikKICApICU+JQogIGxheW91dCgKICAgIHNjZW5lID0gbGlzdCgKICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIngiLCByYW5nZSA9IHhfcmFuZ2UpLAogICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAieSIsIHJhbmdlID0geV9yYW5nZSksCiAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJWYWx1ZSIsIHJhbmdlID0gel9yYW5nZSksCiAgICAgIGFzcGVjdHJhdGlvID0gbGlzdCh4ID0gMi40LCB5ID0gMS4yLCB6ID0gMS4yKSwKICAgICAgICAgICBjYW1lcmEgPSBsaXN0KAogICAgICBleWUgPSBsaXN0KHggPSAxLjUsIHkgPSAxLjUsIHogPSAxKSwgICMgQWRqdXN0IHRoZSB2aWV3cG9pbnQKICAgICAgY2VudGVyID0gbGlzdCh4ID0gMCwgeSA9IDAsIHogPSAwKSkKICAgICksCiAgICB1cGRhdGVtZW51cyA9IGxpc3QoCiAgICAgIGxpc3QoCiAgICAgICAgdHlwZSA9ICJidXR0b25zIiwgc2hvd2FjdGl2ZSA9IEZBTFNFLAogICAgICAgIGJ1dHRvbnMgPSBsaXN0KAogICAgICAgICAgbGlzdChsYWJlbCA9ICJQbGF5IiwgbWV0aG9kID0gImFuaW1hdGUiLAogICAgICAgICAgICAgICBhcmdzID0gbGlzdChOVUxMLCBsaXN0KGZyYW1lID0gbGlzdChkdXJhdGlvbiA9IDEwMCwgcmVkcmF3ID0gVFJVRSksIGZyb21jdXJyZW50ID0gVFJVRSkpKSwKICAgICAgICAgIGxpc3QobGFiZWwgPSAiUGF1c2UiLCBtZXRob2QgPSAiYW5pbWF0ZSIsCiAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KE5VTEwsIGxpc3QobW9kZSA9ICJpbW1lZGlhdGUiLCBmcmFtZSA9IGxpc3QoZHVyYXRpb24gPSAwKSwgcmVkcmF3ID0gRkFMU0UpKSkKICAgICAgICApCiAgICAgICkKICAgICksCiAgICB0aXRsZSA9ICJUaW1lOiAwIgogICkKCiMgQ29udmVydCB0byBwbG90bHkgb2JqZWN0IHdpdGggZnJhbWUgaW5mbwpwYiA8LSBwbG90bHlfYnVpbGQocCkKCiMgSW5qZWN0IGN1c3RvbSB0aXRsZXMgaW50byBlYWNoIGZyYW1lCmZvciAoaSBpbiBzZXFfYWxvbmcocGIkeCRmcmFtZXMpKSB7CiAgdCA8LSB0aW1lX3NlcVtpXQogIGVyciA8LSBzaWduaWYobWF4X2Vycm9yX2F0X2VhY2hfdGltZVtpXSwgNCkKICBwYiR4JGZyYW1lc1tbaV1dJGxheW91dCA8LSBsaXN0KHRpdGxlID0gcGFzdGUwKCJUaW1lOiAiLCB0LCAiIHwgTWF4IEVycm9yOiAiLCBlcnIpKQp9CmBgYAoKCmBgYHtyfQpmaWcgICMgRGlzcGxheSB0aGUgcGxvdApgYGAKCgpgYGB7ciwgZmlnLmhlaWdodCA9IDYsIG91dC53aWR0aCA9ICIxMDAlIiwgZmlnLmNhcCA9IGNhcHRpb25lcigiQ2FwdGlvbiIpfQpwYgpgYGAKCgo=